package com.googlecode.mcwalle;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;

import javax.jms.IllegalStateException;

import com.googlecode.mcwalle.response.ItemInfo;
import com.googlecode.mcwalle.response.StatsCachedumpResponse;
import com.googlecode.mcwalle.response.StatsSlabsResponse;
import com.googlecode.mcwalle.response.TextLineResponse;

/**
 * @author Li Weibo (weibo.leo@gmail.com) //I believe spring-brother
 * @since 2011-10-28 下午04:35:11
 */
public class McClientImpl implements McClient{

	private static final String CRLF = "\r\n";
	
	private final int soTimeout = 2000;
	
	private final boolean tcpNoDelay = true;
	
	private String host;
	
	private int port;
	
	private Socket socket;
	
	private InputStream inputStream;
	private OutputStream outputStream;
	
	public McClientImpl(String host, int port) {
		this.host = host;
		this.port = port;
	}
	
	@Override
	public void destroy() {
		if (!socket.isClosed()) {
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@Override
	public void gc() {
		StatsSlabsResponse res = statsSlabs();
		Iterator<Integer> iter = res.slabNumberIterator();
		
		long totalReleasedBytes = 0;
		long totalReleasedItems = 0;
		
		while (iter.hasNext()) {
			int slabNumber = iter.next();
			StatsCachedumpResponse cacheDumpRes = statsCacheDump(slabNumber, 1000);
			for (ItemInfo info : cacheDumpRes) {
				if (info.isExpired()) {
					boolean deleted = delete(info.getKey());
					if (deleted) {	//对过期数据的删除操作应该直接return false，如果为true，可能是哪里出了问题
						throw new RuntimeException("delected return true on key: " + info.getKey());
					}
					System.out.println(info);
					totalReleasedBytes += info.getBytes();
					totalReleasedItems ++;
				}
			}
			break;
		}
		
		
		System.out.println("done");
		System.out.println(totalReleasedBytes + " bytes released");
		System.out.println(totalReleasedItems + " items released");
	}
	
	public boolean delete(String key) {
		sendCommand(Command.DELETE + " " + key);
		TextLineResponse res = new TextLineResponse(inputStream);
		String line = res.readOneLine();
		if (line.equals("DELETED")) {
			return true;
		} else if (line.equals("NOT_FOUND")) {
			return false;
		} else {
			throw new RuntimeException("Unexpected return: " + line);
		}
	}

	public StatsCachedumpResponse statsCacheDump(int slabNumber, int limit) {
		sendCommand(Command.STATS_CACHEDUMP + " " + slabNumber + " " + limit);
		return new StatsCachedumpResponse(new TextLineResponse(inputStream));
	}
	
	
	public StatsSlabsResponse statsSlabs() {
		sendCommand(Command.STATS_SLABS.toString());
		TextLineResponse res = new TextLineResponse(inputStream);
		return new StatsSlabsResponse(res);
	}
	
	protected void sendCommand(String command) {
		try {
			outputStream.write((command + CRLF).getBytes());
			outputStream.flush();
		} catch (IOException e) {
			throw new RuntimeException("Error while sending command: " + command, e);
		}
	}
	
	@Override
	public void init() {
		try {
			socket = getSocket(host, port, soTimeout, tcpNoDelay);
			inputStream = socket.getInputStream();
			outputStream = socket.getOutputStream();
		} catch (Exception e) {
			throw new RuntimeException("init faild", e);
		}
	}

	public Socket getSocket(String host, int port, int soTimeout, boolean tcpNoDelay) throws UnknownHostException, IOException {
			Socket sock = new Socket(host, port);
			sock.setSoTimeout(soTimeout);
			sock.setTcpNoDelay(tcpNoDelay);
			return sock;
	}
	
	public static void main(String[] args) {
		McClientImpl client = new McClientImpl("10.3.22.59", 11211);
		client.init();
		//client.statsSlabs();
		client.gc();
		
		client.destroy();
	}
}
